//
//  LHELLIOKitDumper.m
//  加密
//
//  Created by iOS Developer 2 on 13/01/15.
//  Copyright © 2015年 欢欢大帅哥. All rights reserved.
//
#import "LHELLIOKitDumper.h"
#import <mach/mach_host.h>
#import <mach/mach_port.h>
#import <dlfcn.h>
#include <objc/runtime.h>
#import "IOKitLib.h"
#import "IOMedia.h"


#define kkIOServicePlane "IOService"
// 这些看不懂的玩意儿都是个int或者unsign int而已,mach为osX与ios的系统内核
typedef mach_port_t io_object_t;/* Used by mach */
typedef io_object_t io_registry_entry_t;
typedef io_object_t io_iterator_t;
typedef char io_name_t[128];
typedef UInt32 IOOptionBits;

io_object_t IOIteratorNext(io_iterator_t iterator);

kern_return_t IOObjectRelease(io_object_t object);

kern_return_t IORegistryEntryGetNameInPlane(io_registry_entry_t entry,
                                            const io_name_t plane,
                                            io_name_t name);

kern_return_t IOMasterPort(mach_port_t bootstrapPort,
                           mach_port_t *masterPort);

io_registry_entry_t IORegistryGetRootEntry(mach_port_t masterPort);

kern_return_t IORegistryEntryCreateCFProperties(io_registry_entry_t entry,
                                                CFMutableDictionaryRef *properties,
                                                CFAllocatorRef allocator,
                                                IOOptionBits options);


boolean_t IOObjectConformsTo(io_object_t object,
                             const io_name_t className);


kern_return_t IORegistryEntryGetChildIterator(io_registry_entry_t entry,
                                              const io_name_t plane,
                                              io_iterator_t *iterator);
// joey
CFTypeRef IORegistryEntrySearchCFProperty ( io_registry_entry_t entry, const io_name_t plane, CFStringRef key, CFAllocatorRef allocator, IOOptionBits options );
const UInt32 kIORegFlagShowProperties = (1 << 1);

struct options {
    char *class;
    UInt32 flags;
    char *name;
    char *plane;
};

static void assertion(int condition, char *message) {
    if (condition == 0) {
        fprintf(stderr, "ioreg: error: %s.\n", message);
        exit(1);
    }
}

const char *path="/System/Library/Frameworks/IOKit.framework/IOKit";

typedef kern_return_t (*JoeyIOMasterPortDecl)(mach_port_t bootstrapPort, mach_port_t *masterPort);
typedef io_registry_entry_t (*JoeyIORegistryGetRootEntryDecl)(mach_port_t masterPort);
typedef io_object_t (*JoeyIOIteratorNextDecl)(io_iterator_t iterator);
typedef kern_return_t (*JoeyIOObjectReleaseDecl)(io_object_t object);
typedef kern_return_t (*JoeyIORegistryEntryGetNameInPlaneDecl)(io_registry_entry_t entry, const io_name_t plane, io_name_t name);
typedef kern_return_t (*JoeyIORegistryEntryCreateCFPropertiesDecl)(io_registry_entry_t entry, CFMutableDictionaryRef *properties, CFAllocatorRef allocator, IOOptionBits options);
typedef boolean_t (*JoeyIOObjectConformsToDecl)(io_object_t object, const io_name_t className);
typedef kern_return_t (*JoeyIORegistryEntryGetChildIteratorDecl)(io_registry_entry_t entry, const io_name_t plane, io_iterator_t *iterator);
typedef CFTypeRef (*JoeyIORegistryEntrySearchCFProperty) ( io_registry_entry_t entry, const io_name_t plane, CFStringRef key, CFAllocatorRef allocator, IOOptionBits options );

static JoeyIOMasterPortDecl sIOMasterPort = nil;
static JoeyIORegistryGetRootEntryDecl sIORegistryGetRootEntry = nil;
static JoeyIOIteratorNextDecl sIOIteratorNext = nil;
static JoeyIOObjectReleaseDecl sIOObjectRelease = nil;
static JoeyIORegistryEntryGetNameInPlaneDecl sIORegistryEntryGetNameInPlane = nil;
static JoeyIORegistryEntryCreateCFPropertiesDecl sIORegistryEntryCreateCFProperties = nil;
static JoeyIOObjectConformsToDecl sIOObjectConformsTo = nil;
static JoeyIORegistryEntryGetChildIteratorDecl sIORegistryEntryGetChildIterator = nil;
static JoeyIORegistryEntrySearchCFProperty sIORegistryEntrySearchCFProperty = nil;
@implementation LHELLIOKitDumper
/**
 *  iokit初始化方法
 *
 *  @return
 */
BOOL ioKitInitialize()
{
    static BOOL inited = FALSE;
    if (!inited) {
        
        char path[256], func1[64], func2[64], func3[64], func4[64], func5[64], func6[64], func7[64], func8[64], func9[64];
        sprintf(path, "%s%s%s%s%s%s%s%s%s", "/Sys", "tem/L", "ibrar", "y/Framewo", "rks/I", "OKi", "t.fram", "ework/IO", "Kit");
        sprintf(func1, "%s%s%s%s", "I", "OM", "ast", "erPort");
        sprintf(func2, "%s%s%s%s%s", "IO", "Regi", "stryGe", "tRoot", "Entry");
        sprintf(func3, "%s%s%s", "IOI", "terat", "orNext");
        sprintf(func4, "%s%s%s%s", "IO", "Ob", "jectR", "elease");
        sprintf(func5, "%s%s%s%s%s%s", "IORe", "gistr", "yEntryG", "etNa", "meInP", "lane");
        sprintf(func6, "%s%s%s%s%s%s", "I", "ORegi", "stryEn", "tryCreat", "eCFProp", "erties");
        sprintf(func7, "%s%s%s%s", "IOO", "bjectC", "onform", "sTo");
        sprintf(func8, "%s%s%s%s%s%s", "IO", "Re", "gistryEn", "tryGetC", "hildIter", "ator");
        sprintf(func9, "%s%s%s%s%s%s", "IOR", "egist", "ryEntryS", "earc", "hCFPrope", "rty");
        
        
        void* IOKit = dlopen(path, RTLD_LAZY);
        if (!IOKit) return inited;
        
        sIOMasterPort = (JoeyIOMasterPortDecl)dlsym(IOKit, func1);
        if (!sIOMasterPort) return inited;
        sIORegistryGetRootEntry = (JoeyIORegistryGetRootEntryDecl)dlsym(IOKit, func2);
        if (!sIORegistryGetRootEntry) return inited;
        sIOIteratorNext = (JoeyIOIteratorNextDecl)dlsym(IOKit, func3);
        if (!sIOIteratorNext) return inited;
        sIOObjectRelease = (JoeyIOObjectReleaseDecl)dlsym(IOKit, func4);
        if (!sIOObjectRelease) return inited;
        sIORegistryEntryGetNameInPlane = (JoeyIORegistryEntryGetNameInPlaneDecl)dlsym(IOKit, func5);
        if (!sIORegistryEntryGetNameInPlane) return inited;
        sIORegistryEntryCreateCFProperties = (JoeyIORegistryEntryCreateCFPropertiesDecl)dlsym(IOKit, func6);
        if (!sIORegistryEntryCreateCFProperties) return inited;
        sIOObjectConformsTo = (JoeyIOObjectConformsToDecl)dlsym(IOKit, func7);
        if (!sIOObjectConformsTo) return inited;
        sIORegistryEntryGetChildIterator = (JoeyIORegistryEntryGetChildIteratorDecl)dlsym(IOKit, func8);
        if (!sIORegistryEntryGetChildIterator) return inited;
        sIORegistryEntrySearchCFProperty =(JoeyIORegistryEntrySearchCFProperty)dlsym(IOKit, func9);
        if (!sIORegistryEntrySearchCFProperty) return inited;
        
        inited = TRUE;
    }
    return inited;
}

// MARK:对root进行节点遍历，层层递归得到一个含有所有属性和所有子节点的一个数据模型。
- (NSMutableArray *)JoeydumpIOKitTree {
    mach_port_t iokitPort = 0;
    struct options options;
    io_registry_entry_t service = 0;
    kern_return_t status = KERN_SUCCESS; // 0
    
    // 初始化iokitFUNC
    ioKitInitialize();
    options.class = 0;
    options.flags = kIORegFlagShowProperties;//??
    options.name = 0;
    options.plane = kkIOServicePlane;// ??
    
    // Returns the mach port used to initiate communication with IOKit.
    // 1.MARK:获取iokitPort
    status = sIOMasterPort(bootstrap_port,&iokitPort);
    // 2.验证
    assertion(status == KERN_SUCCESS, "can't obtain I/O Kit's master port");
    // Return a handle to the registry root
    // 3.获取 iokit中io_registry_entry_t
    service = sIORegistryGetRootEntry(iokitPort);
    // 4.验证
    assertion(service, "can't obtain I/O Kit's root service");
    // 5.遍历节点
    NSMutableArray *root = [self _scanService:service options:options];
    
    sIOObjectRelease(service);
    
    return root;
}
// MARK:遍历的root下的service ，返回一个节点，节点中包含其所有properties 和 children节点
- (NSMutableArray *)_scanService:(io_registry_entry_t)service options:(struct options)options {
    
    io_registry_entry_t child = 0;
    io_registry_entry_t childUpNext = 0;
    io_iterator_t children = 0;
    kern_return_t status = KERN_SUCCESS;
    
    // Obtain the service's children.
    // Returns an iterator over an registry entry's child entries in a plane.
    /**
     *  获取迭代器的子条目
     *
     *  @param service       io_registry_entry_t
     *  @param options.plane 指定的plane
     *  @param children      创建迭代器的子条目，完成之后迭代器必须释放
     *
     *  @return
     */
    status = sIORegistryEntryGetChildIterator(service, options.plane, &children);
    assertion(status == KERN_SUCCESS, "can't obtain children");
    
    // 在children中获取下一层级的ioOBJC
    childUpNext = sIOIteratorNext(children);
    // 取到parent节点下的所有内容
    NSMutableArray *node = [self _showService:service options:options];
    
    // Traverse over the children of this service.
    // 遍历！！
    while (childUpNext) {
        child = childUpNext;
        
        childUpNext = sIOIteratorNext(children);
        
        NSMutableArray *childNode = [self _scanService:child options:options];
        if (childNode) {
            [node addObject:childNode];
        }
        sIOObjectRelease(child);
    }
    // Releases an object handle previously returned by IOKit.
    
    sIOObjectRelease(children);
    
    children = 0;
    
    return node;
    
}
// MARK:获取单个节点下的所有property，返回一个数据模型
- (NSMutableArray *)_showService:(io_registry_entry_t)service options:(struct options)options {
    io_name_t name;
    CFMutableDictionaryRef properties = 0;
    //    CFMutableArrayRef arr = 0;
    NSMutableArray *arr = [NSMutableArray new];
    kern_return_t status = KERN_SUCCESS;
    /*
     *  返回一个指定的plane中的registry_entry_t的C string，在这个函数中可以取到指定plane中的节点名称name;
     *
     *  @param service       io_registry_entry_t ,从 IOMasterPort 返回的iokitport中利用 IORegistryGetRootEntry（iokitPort）调出
     *  @param options.plane The name of an existing registry plane. Plane names are defined in IOKitKeys.h, eg. kkIOServicePlane.
     *  @param name          节点名称
     *
     *  @return c string
     */
    status = sIORegistryEntryGetNameInPlane(service, options.plane, name);
    
    if(status != KERN_SUCCESS) return nil;
    assertion(status == KERN_SUCCESS, "can't obtain name");
    
    //    NSMutableArray *translatedProperties = [NSMutableArray new];
    NSMutableString *translatedProperties = [NSMutableString new];
    
    /**
     *  这个函数使用OSMetaClass系统内核来确定对象动态类,如果是这类或子类的对象，指定为c字库。
     *
     *  @param service       io_registry_entry_t
     *  @param options.class 类名
     *
     *  @return 返回一个bool
     */
    // 动态类型转换，如果对象处理是有效的,代表一个对象在内核中,动态类型转换类返回真,否则为假。
    if (options.class && sIOObjectConformsTo(service, options.class)) {
        options.flags |= kIORegFlagShowProperties;
    }
    // strcmp为字符串比较
    if (options.name && !strcmp(name, options.name)) {
        // 应该是用算法来标识
        options.flags |= kIORegFlagShowProperties;
    }
    
    if (options.flags & kIORegFlagShowProperties) {
        // Obtain the service's properties.
        // Create a CF dictionary representation of a registry entry's property table.
        // 成功后返回值应为0
        status = sIORegistryEntryCreateCFProperties(service,
                                                    &properties,
                                                    kCFAllocatorDefault,
                                                    kNilOptions);
        
        // 判断是否返回CFpropertie成功
        assertion(status == KERN_SUCCESS, "can't obtain properties");
        // 通过类型表示符判断properties是否为CFDictionary,应该相当于isKindOfClass
        assertion(CFGetTypeID(properties) == CFDictionaryGetTypeID(), NULL);
        /**
         *  Calls a function once for each value in the dictionary.每一次调用dictionary都会调用这个函数
         *
         *  @param properties               dictionary
         *  @param CFDictionaryShow_Applier 回调函数，其中可以取到dic的key, value,以及user defined pramater。
         *  @param void                     CFDictionaryShow_Applier 中的user defined parmater
         *
         *  @return
         */
        CFDictionaryApplyFunction(properties, CFDictionaryShow_Applier, (__bridge void *) (translatedProperties));
        
        CFRelease(properties);
    }
    /**
     将parent这个节点中name和含有key value的数组properties转换为模型数据
     
     :returns: 返回这个模型节点
     */
    //NSDictionary *Dic = [NSDictionary dictionaryWithObject:translatedProperties forKey:[NSString stringWithCString:name encoding:NSUTF8StringEncoding]];
    NSString *mStr = [NSString stringWithFormat:@"\n%@\n%@",[NSString stringWithCString:name encoding:NSUTF8StringEncoding],translatedProperties];
    [arr addObject:mStr];
    
    return arr;
}
// MARK:搜索IOProperty的方法
- (NSString *)searchIOProperty:(NSString *)searchKey{
    if (!ioKitInitialize())return nil;
    mach_port_t masterPort;
    //    CFTypeID propID = (CFTypeID) NULL;
    //    long bufSize;
    NSString* prop = nil;
    
    // ioKitInitialize();
    kern_return_t kr = sIOMasterPort(MACH_PORT_NULL, &masterPort);
    if (kr != noErr) return nil;
    io_registry_entry_t entry = sIORegistryGetRootEntry(masterPort);
    if (entry == MACH_PORT_NULL) return nil;
    CFTypeRef propAsCFString = sIORegistryEntrySearchCFProperty(entry, kkIOServicePlane, (__bridge CFStringRef) searchKey, nil, kIORegistryIterateRecursively);
    
    if (propAsCFString) {
        prop = (__bridge_transfer NSString*) propAsCFString;
        NSString *type = [NSString stringWithFormat:@"%@",[prop class]];
        // DDLogDebug(@"%@",type);
        if ([type isEqualToString:@"__NSCFData"] || [type isEqualToString:@"NSConcreteData"]) {
            prop = [[NSString alloc] initWithData:(__bridge NSData *)(propAsCFString) encoding:NSUTF8StringEncoding];
        }
        
    }
    mach_port_deallocate(mach_task_self(), masterPort);
    
    
    return prop;
}

+ (NSString *)searchIOProperty:(NSString *)searchKey{
    LHELLIOKitDumper *dumper = [LHELLIOKitDumper new];
    NSString *str = [dumper searchIOProperty:searchKey];
    return str;
}

static void CFArrayShow_Applier(const void *value, void *parameter) {
    NSMutableArray *translatedElements = (__bridge NSMutableArray *) parameter;
    NSString *translatedElement = CFObjectShow(value);
    
    if (translatedElement) {
        [translatedElements addObject:translatedElement];
    }
}

// MARK:在这个回调方法中将key和value拼接，用自定义参数 parameter 装载
static void CFDictionaryShow_Applier(const void *key, const void *value, void *parameter) {
    
    //    NSMutableArray *translatedElements = (__bridge NSMutableArray *) (parameter);
    NSMutableString *translatedElements = (__bridge NSMutableString *) (parameter);
    NSString *name = CFObjectShow(key);
    NSString *val = CFObjectShow(value);
    
    if (name) {
        //        [translatedElements addObject:[NSString stringWithFormat:@"%@ = %@", name, val ?: @"<Null>"]];
        [translatedElements appendString:[NSString stringWithFormat:@"\t%@ = %@\n", name, val ?: @"<Null>"]];
        
    }
}

static void CFSetShow_Applier(const void *value, void *parameter) {
    NSMutableArray *translatedElements = (__bridge NSMutableArray *) (parameter);
    NSString *objectValue = CFObjectShow(value);
    
    if (objectValue) {
        [translatedElements addObject:objectValue];
    }
}

static NSString *CFArrayShow(CFArrayRef object) {
    CFRange range = {0, CFArrayGetCount(object)};
    NSMutableArray *translatedElements = [NSMutableArray new];
    CFArrayApplyFunction(object, range, CFArrayShow_Applier, (__bridge void *) (translatedElements));
    
    return [NSString stringWithFormat:@"%@", [translatedElements componentsJoinedByString:@""]];
}

static NSString *CFBooleanShow(CFBooleanRef object) {
    return CFBooleanGetValue(object) ? @"Yes" : @"No";
}

static NSString *CFDataShow(CFDataRef object) {
    UInt32 asciiNormalCount = 0;
    UInt32 asciiSymbolCount = 0;
    const UInt8 *bytes;
    CFIndex index;
    CFIndex length;
    
    NSMutableString *result = [[NSMutableString alloc] initWithString:@"<"];
    
    length = CFDataGetLength(object);
    bytes = CFDataGetBytePtr(object);
    
    //
    // This algorithm detects ascii strings, or a set of ascii strings, inside a
    // stream of bytes.  The string, or last string if in a set, needn't be null
    // terminated.  High-order symbol characters are accepted, unless they occur
    // too often (80% of characters must be normal).  Zero padding at the end of
    // the string(s) is valid.  If the data stream is only one byte, it is never
    // considered to be a string.
    //
    
    for (index = 0; index < length; index++) {  // (scan for ascii string/strings)
        
        if (bytes[index] == 0) {      // (detected null in place of a new string,
            //  ensure remainder of the string is null)
            for (; index < length && bytes[index] == 0; index++) {}
            
            break;          // (either end of data or a non-null byte in stream)
        } else {                       // (scan along this potential ascii string)
            
            for (; index < length; index++) {
                if (isprint(bytes[index])) {
                    asciiNormalCount++;
                } else if (bytes[index] >= 128 && bytes[index] <= 254) {
                    asciiSymbolCount++;
                } else {
                    break;
                }
            }
            
            if (index < length && bytes[index] == 0) {        // (end of string)
                continue;
            } else {
                break;
            }
        }
    }
    
    if ((asciiNormalCount >> 2) < asciiSymbolCount) {  // (is 80% normal ascii?)
        index = 0;
    } else if (length == 1) {                                // (is just one byte?)
        index = 0;
    }
    
    if (index >= length && asciiNormalCount) { // (is a string or set of strings?)
        Boolean quoted = FALSE;
        
        for (index = 0; index < length; index++) {
            if (bytes[index]) {
                if (quoted == FALSE) {
                    quoted = TRUE;
                    if (index) {
                        [result appendString:@",\""];
                    } else {
                        [result appendString:@"\""];
                    }
                }
                [result appendFormat:@"%c", bytes[index]];
            } else {
                if (quoted == TRUE) {
                    quoted = FALSE;
                    [result appendString:@"\""];
                } else {
                    break;
                }
            }
        }
        if (quoted == TRUE) {
            [result appendString:@"\""];
        }
    } else {                                 // (is not a string or set of strings)
        for (index = 0; index < length; index++) {
            [result appendFormat:@"%02x", bytes[index]];
        }
    }
    
    [result appendString:@">"];
    return result;
}

static NSString *CFDictionaryShow(CFDictionaryRef object) {
    NSMutableString *translatedElements = [NSMutableString new];
    
    CFDictionaryApplyFunction(object, CFDictionaryShow_Applier, (__bridge void *) (translatedElements));
    
    //    return [NSString stringWithFormat:@"{%@}", [translatedElements componentsJoinedByString:@""]];
    return [NSString stringWithFormat:@"{%@}", translatedElements];
}

static NSString *CFNumberShow(CFNumberRef object) {
    long long number;
    
    if (CFNumberGetValue(object, kCFNumberLongLongType, &number)) {
        return [NSString stringWithFormat:@"%qd", number];
    }
    return @"<Nan>";
}

static NSString *CFObjectShow(CFTypeRef object) {
    CFTypeID type = CFGetTypeID(object);
    
    if (type == CFArrayGetTypeID()) return CFArrayShow(object);
    else if (type == CFBooleanGetTypeID()) return CFBooleanShow(object);
    else if (type == CFDataGetTypeID()) return CFDataShow(object);
    else if (type == CFDictionaryGetTypeID()) return CFDictionaryShow(object);
    else if (type == CFNumberGetTypeID()) return CFNumberShow(object);
    else if (type == CFSetGetTypeID()) return CFSetShow(object);
    else if (type == CFStringGetTypeID()) return CFStringShow(object);
    else return @"<unknown object>";
}

static NSString *CFSetShow(CFSetRef object) {
    NSMutableArray *translatedElements = [NSMutableArray new];
    CFSetApplyFunction(object, CFSetShow_Applier, (__bridge void *) (translatedElements));
    return [NSString stringWithFormat:@"%@", [translatedElements componentsJoinedByString:@""]];
}

static NSString *CFStringShow(CFStringRef object) {
    
    NSString *stringToShow = @"";
    
    const char *c = CFStringGetCStringPtr(object, kCFStringEncodingMacRoman);
    
    if (c) {
        return [NSString stringWithFormat:@"%s", c];
    } else {
        CFIndex bufferSize = CFStringGetLength(object) + 1;
        char *buffer = malloc(bufferSize);
        
        if (buffer) {
            if (CFStringGetCString(object, buffer, bufferSize, kCFStringEncodingMacRoman)) {
                stringToShow = [NSString stringWithFormat:@"%s", buffer];
            }
            
            free(buffer);
        }
    }
    return stringToShow;
}

@end
